﻿using System;
using System.Runtime.Serialization;
using System.Threading;

namespace Close2Poco
{
    //TODO: write some unit tests for these classes

    [Serializable]
    public class LockTimeoutException: Exception
    {
        public LockTimeoutException(string message): base(message)
        {}

        public LockTimeoutException(SerializationInfo info, StreamingContext context):base(info, context)
        {}

        public const int TimeoutMilliseconds = 30 * 1000;  //this is 30 seconds - a really long time, but short enough to prevent permanent lockups
    }

    /// <summary>
    /// Helps with using <see cref="ReaderWriterLockSlim"/>
    /// </summary>
    /// <remarks>Loosely based on code from http://www.interact-sw.co.uk/iangblog/2004/04/26/yetmoretimedlocking (but with much simplifaction).
    /// Gives us two things: (a) a conventient "using" syntax for using ReaderWriterLockSlim and (b) conventient 
    /// support for the "TryEnter" based lock acquistion, which prevents permanent deadlocks from ever happening and baffling our debugging attempts! (Get an execption instead with TryEnter...Lock)</remarks>
    public struct ReadLock: IDisposable
    {
        public ReadLock(ReaderWriterLockSlim readerWriterLock)
        {
            _lock = readerWriterLock;
            if (!_lock.TryEnterReadLock(LockTimeoutException.TimeoutMilliseconds))
                throw new LockTimeoutException("Timed out waiting for read lock");
        }

        readonly ReaderWriterLockSlim _lock;

        public void Dispose()
        {
            _lock.ExitReadLock();  
            // *** Readme ***
            // If you get a null reference here, it is probably because you mistakenly used the parameterless 
            // constructor, instead of passing a a ReaderWriterLockSlim
        }
    }

    /// <summary>
    /// See the Write equivalent of <see cref="ReadLock"/>
    /// </summary>
    public struct WriteLock : IDisposable
    {
        public WriteLock(ReaderWriterLockSlim readerWriterLock)
        {
            _lock = readerWriterLock;
            if (!_lock.TryEnterWriteLock(LockTimeoutException.TimeoutMilliseconds))
                throw new LockTimeoutException("Timed out waiting for write lock");
        }

        readonly ReaderWriterLockSlim _lock;

        public void Dispose()
        {
            _lock.ExitWriteLock();
            // *** Readme ***
            // If you get a null reference here, it is probably because you mistakenly used the parameterless 
            // constructor, instead of passing a a ReaderWriterLockSlim
        }
    }

}
